package com.android.baseconfig.base;

import android.annotation.SuppressLint;
import android.util.Log;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import androidx.annotation.NonNull;

import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.functions.Consumer;
import io.reactivex.rxjava3.subjects.PublishSubject;
import io.reactivex.rxjava3.subjects.Subject;

/**
 * -----------------------------------------------------------------
 * Copyright © 2016年 clife. All rights reserved.
 * Shenzhen H&T Intelligent Control Co.,Ltd.
 * -----------------------------------------------------------------
 * File: RxBus.java
 * Create: 2016/6/20 20:26
 * Author: uuxia
 */
public class RxBus3 {
    private static RxBus3 instance;
    @SuppressWarnings("rawtypes")
    private ConcurrentHashMap<Object, List<Subject>> subjectMapper = new ConcurrentHashMap<>();

    private ConcurrentHashMap<Object, ConcurrentHashMap<Object, Observable<?>>> subcriber = new ConcurrentHashMap<>();

    private ConcurrentHashMap<Object, List<Disposable>> subscriptionMapper = new ConcurrentHashMap<>();

    private RxBus3() {
    }

    public static synchronized RxBus3 $1() {
        if (null == instance) {
            instance = new RxBus3();
        }
        return instance;
    }

    public static RxBus3 $() {
        return getInstance();
    }

    public static RxBus3 getInstance() {
        if (null == instance) {
            synchronized (RxBus3.class) {
                if (instance == null) {
                    instance = new RxBus3();
                }
            }
        }
        return instance;
    }

    @SuppressWarnings("rawtypes")
    public static boolean isEmpty(Collection<Subject> collection) {
        return null == collection || collection.isEmpty();
    }

    /**
     * 订阅事件源
     *
     * @param mObservable
     * @param consumer
     * @return
     */
    @SuppressLint("CheckResult")
    public RxBus3 OnEvent(Observable<?> mObservable, Consumer<Object> consumer) {
        mObservable.observeOn(AndroidSchedulers.mainThread())
                .subscribe(consumer, Throwable::printStackTrace);
        return $();
    }

    /**
     * 注册tag事件源
     * 建议不要使用该方法
     *
     * @param tag
     * @return
     */
    @SuppressWarnings({"rawtypes"})
    public <T> Observable<T> register(@NonNull Object tag) {
        List<Subject> subjectList = subjectMapper.get(tag);
        if (null == subjectList) {
            subjectList = new ArrayList<Subject>();
            subjectMapper.put(tag, subjectList);
        }
        Subject<T> subject;
        subjectList.add(subject = PublishSubject.create());
        //Logc.d("uuok.register" + tag + "  size:" + subjectList.size());
        return subject;
    }

    /**
     * 注册tag事件源
     *
     * @param tag
     * @param consumer
     * @param <T>
     * @return
     */
    @SuppressLint("CheckResult")
    @SuppressWarnings({"rawtypes"})
    public <T> Observable<T> register(@NonNull Object tag, @NonNull Consumer<T> consumer) {
        List<Subject> subjectList = subjectMapper.get(tag);
        if (null == subjectList) {
            subjectList = new ArrayList<Subject>();
            subjectMapper.put(tag, subjectList);
        }
        Subject<T> subject;
        subjectList.add(subject = PublishSubject.create());
        // Log.d("RxBus", "uuok.register" + tag + "  size:" + subjectList.size());
        subject.observeOn(AndroidSchedulers.mainThread()).subscribe(consumer, throwable -> throwable.printStackTrace());
        return subject;
    }

    /**
     * 注册tag事件源,并指定class
     *
     * @param tag
     * @param consumer
     * @param clasz
     * @param <T>
     * @return
     */
    public <T> Observable<T> register(@NonNull Object tag, @NonNull Consumer<T> consumer, Class clasz) {
        Log.d("RxBus", "uuu %% " + clasz.toString());
        List<Subject> subjectList = subjectMapper.get(tag);
        if (null == subjectList) {
            subjectList = new ArrayList<Subject>();
            subjectMapper.put(tag, subjectList);
        }
        Subject<T> subject;
        subjectList.add(subject = PublishSubject.create());
        //Logc.d("uuok.register" + tag + "  size:" + subjectList.size());
        //        subject.observeOn(AndroidSchedulers.mainThread()).subscribe(consumer, (e) -> e.printStackTrace());
        Disposable sub = subject.observeOn(AndroidSchedulers.mainThread())
                .subscribe(consumer, Throwable::printStackTrace);
        List<Disposable> subList = subscriptionMapper.get(clasz);
        if (null == subList) {
            subList = new ArrayList<>();
        }
        subList.add(sub);
        subscriptionMapper.put(clasz, subList);
        ConcurrentHashMap<Object, Observable<?>> subMap = subcriber.get(clasz);
        if (subMap == null) {
            subMap = new ConcurrentHashMap<>();
        }
        subMap.put(tag, subject);
        subcriber.put(clasz, subMap);
        return subject;
    }

    public void clear(Class clazz) {
        unsubscribe(clazz);
        ConcurrentHashMap<Object, Observable<?>> sub = subcriber.get(clazz);
        if (sub != null) {
            for (Object tag : sub.keySet()) {
                if (tag != null) {
                    Observable<?> value = sub.get(tag);
                    if (value != null) {
                        unregister(tag, value);
                    }
                }
            }
        }
    }

    private void unsubscribe(Class clazz) {
        List<Disposable> sub = subscriptionMapper.remove(clazz);
        if (sub != null && !sub.isEmpty()) {
            Iterator<Disposable> it = sub.iterator();
            while (it.hasNext()) {
                Disposable subscriop = it.next();
                subscriop.dispose();
                it.remove();
            }
        }
    }

    /**
     * 删除class里面的所以事件，前提是注册的时候指定了class
     *
     * @param clazz
     */
    public void unregister(@NonNull Class clazz) {
        clear(clazz);
    }

    /**
     * 此方法是删除tag对应的整个事件，其他地方注册的该事件也一并统统删除，操作此方法要慎重考虑
     *
     * @param tag
     */
    @SuppressWarnings("rawtypes")
    public void unregister(@NonNull Object tag) {
        List<Subject> subjects = subjectMapper.get(tag);
        if (null != subjects) {
            subjectMapper.remove(tag);
        }
    }

    /**
     * 删除tag对应事件的指定监听者(observable),而不删除tag事件以及其他监听者
     *
     * @param tag
     * @param observable
     * @return
     */
    @SuppressWarnings("rawtypes")
    public RxBus3 unregister(@NonNull Object tag,
                             @NonNull Observable<?> observable) {
        List<Subject> subjects = subjectMapper.get(tag);
        if (null != subjects) {
            subjects.remove(observable);
            if (isEmpty(subjects)) {
                subjectMapper.remove(tag);
                //Logc.d("uuok.unregister" + tag + "  size:" + subjects.size());
            }
        }
        return $();
    }

    public void post(@NonNull Object content) {
        post(content.getClass().getName(), content);
    }

    /**
     * 触发事件\
     *
     * @param content
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public void post(@NonNull Object tag, @NonNull Object content) {
        // Log.d("RxBus", "post" + "eventName: " + tag);
        List<Subject> subjectList = subjectMapper.get(tag);
        if (!isEmpty(subjectList)) {
            for (int i = 0; i < subjectList.size(); i++) {
                Subject subject = subjectList.get(i);
                if (subject != null) {
                    subject.onNext(content);
                    // Log.d("RxBus", "eventName: " + tag + " subject:" + subject.hasObservers());
                }
            }

        }
    }
}
